from django.contrib.auth.models import User
from django.db import models
import datetime
import json


def import_class(class_name):
    meta_module_path = '.'.join(class_name.split('.')[:-1])
    meta_name = class_name.split('.')[-1]
    meta_module = __import__(meta_module_path, fromlist=[meta_module_path.split('.')[-1]])
    return getattr(meta_module, meta_name)

class ObjectKind(models.Model):
    OBJECT_KIND = (
        ('A', 'Action'),
        ('O', 'Object'),
    )
    name = models.CharField(max_length=32, unique=True)
    clazz = models.CharField(max_length=128)
    type = models.CharField(max_length=1, choices=OBJECT_KIND, default='O')
    def __str__(self):
        return self.name
    def get_clazz(self):
        return import_class(self.clazz)

class MetaAction(models.Model):
    name = models.CharField(max_length=32, unique=True)
    result_object_name = models.CharField(max_length=128, blank=True)
    meta_data = models.TextField(default="")
    owner = models.CharField(max_length=32)
    def __str__(self):
        return self.name
    def get_meta_data(self):
        return json.loads(self.meta_data)
    def set_meta_data(self, meta_data):
        self.meta_data = json.dumps(meta_data)
    def instantiate(self, scenario):
        meta_class = self.object_kind.get_clazz()
        try:
            data = self.get_meta_data()
        except ValueError:
            data = {}
        meta_action = meta_class(data) 
        return meta_action.instantiate(scenario)

class MetaObject(models.Model):
    name = models.CharField(max_length=32, unique=True)
    object_kind = models.ForeignKey(ObjectKind)
    meta_data = models.TextField(default="")
    def __str__(self):
        return self.name
    def get_meta_data(self):
        return json.loads(self.meta_data)
    def set_meta_data(self, meta_data):
        self.meta_data = json.dumps(meta_data)
    def instantiate(self, scenario):
        meta_class = self.object_kind.get_clazz()
        try:
            data = self.get_meta_data()
        except ValueError:
            data = {}
        meta_object = meta_class(data) 
        return meta_object.instantiate(scenario)

class Scenario(models.Model):
    name = models.CharField(max_length=32, unique=True)
    beginning_date = models.DateField(default=datetime.date.today())
    def __str__(self):
        return self.name

class ScenarioTemplate(models.Model):
    name = models.CharField(max_length=128, unique=True)
    meta_objects = models.ManyToManyField(MetaObject)
    meta_actions = models.ManyToManyField(MetaAction, through='TimestampedTemplateAction')
    def __str__(self):
        return self.name
    def instantiate(self, beginning_date):
        scenario = Scenario(name=self.name, beginning_date=beginning_date)
        scenario.save()
        # create objects
        for object in self.meta_objects.all():
            object = InstanceObject(name=object.name,
                                    meta_object=object,
                                    scenario=scenario)
            object.save()
        # create actions
        for template_action in TimestampedTemplateAction.objects.filter(scenario_template=self):
            template_action.instantiate(scenario)
        return scenario

class InstanceObject(models.Model):
    name = models.CharField(max_length=128)
    meta_object = models.ForeignKey(MetaObject)
    scenario = models.ForeignKey(Scenario)
    data = models.TextField()
    def __str__(self):
        return self.name
    class Meta:
        unique_together = (("name", "scenario"),)

class InstanceAction(models.Model):
    name = models.CharField(max_length=128)
    meta_action = models.ForeignKey(MetaAction)
    object = models.ForeignKey(InstanceObject, null=True, blank = True)
    scenario = models.ForeignKey(Scenario)
    real_date = models.DateField()
    real_who = models.ForeignKey(User, null=True, blank=True)
    done = models.BooleanField()
    def __str__(self):
        return self.name
    def action_done(self, user):
        self.done = True
        self.real_who = user
    class Meta:
        unique_together = (("name", "scenario"),)

class TimestampedTemplateAction(models.Model):
    """
    @todo: order the actions
    """
    name = models.CharField(max_length=128)
    action = models.ForeignKey(MetaAction)
    scenario_template = models.ForeignKey(ScenarioTemplate)
    delta = models.IntegerField(default=0)
    def __str__(self):
        return self.name
    class Meta:
        unique_together = (("action", "scenario_template"),)
    def instantiate(self, scenario):
        action = InstanceAction(name=self.name, scenario=scenario)
        action.meta_action = self.action
        action.real_date = scenario.beginning_date + datetime.timedelta(days=self.delta)
        if self.action.result_object_name:
            try:
                object = InstanceObject.objects.get(scenario=scenario, name=self.action.result_object_name)
                action.object = object
            except InstanceObject.DoesNotExist:
                print "object:'%s' doesn't exist in scenario '%'" % \
                      (self.action.result_object_name, scenario.name)
        action.save()
